﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
using System.Xml.Linq;
using SystemX.Infrastructure;

namespace SystemX.Utility
{
    public static partial class XmlUtil
	{
		#region ToXML
		/// <summary>
		/// to XML,same as ToXML{T}.
		/// </summary>
		/// <returns></returns>
		public static String ToXml<T>(this T obj)
		{
			XmlSerializer serial = new XmlSerializer(typeof(T));
			using (MemoryStream stream = new MemoryStream())
			{
				using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8))
				{
					serial.Serialize(writer, obj);
					Byte[] bts = stream.ToArray();
					String xml = Encoding.UTF8.GetString(bts);
					writer.Close();
					if (!String.IsNullOrEmpty(xml))//trick
						xml = xml.Trim();
					return xml;
				}
			}
		}
		/// <summary>
		/// Serialize an object to xml.using utf-16 encoding~ If wanna use uft-8
		/// encoding,<see cref="ToXML{T}(obj,useUTF8)">ToXML{T}(obj,useUTF8)</see>
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="obj"></param>
        public static string ToXML<T>(this T obj)
        {
			return obj.ToXML(false);
        }
		/// <summary>
		/// serialize an object to xml.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="obj">the object to be serialized</param>
		/// <param name="useUTF8">
		/// Yes to use utf-8 encoding,use uft-16 vice versa.
		/// .Net use utf-16 by default when doing the xml serialization.
		/// </param>
		/// <returns></returns>
		public static string ToXML<T>(this T obj, bool useUTF8) {
			XmlSerializer ss = new XmlSerializer(typeof(T));
			StringBuilder sb = new StringBuilder();
			if (useUTF8)
			{
				using (StringWriterWithEncoding sw = new StringWriterWithEncoding(sb, Encoding.UTF8))
				{
					ss.Serialize(sw, obj);
				}
			}
			else {
				using (StringWriter sw = new StringWriter(sb))
				{
					ss.Serialize(sw, obj);
				}
			}
			return sb.ToString().Trim();
		}
        
        /// <summary>
        /// to XmlDocument.use utf-16 encoding.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static XmlDocument ToXMLDoc<T>(this T obj) {
			return obj.ToXMLDoc(false);
        }
		/// <summary>
		/// to XmlDocument
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="obj"></param>
		/// <returns></returns>
		public static XmlDocument ToXMLDoc<T>(this T obj,bool useUTF8)
		{
			string xmlStr = ToXML(obj,useUTF8);
			XmlDocument doc = new XmlDocument();
			doc.LoadXml(xmlStr);
			return doc;
		}
		/// <summary>
		/// Convert a StringDictionary to a xml document.
		/// </summary>
		/// <param name="dic"></param>
		/// <param name="rootName"></param>
		/// <returns></returns>
        public static XDocument ToXMLDoc(this StringDictionary dic, string rootName)
        {
            XDocument doc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));

            var items = new List<XElement>();
            foreach (DictionaryEntry item in dic)
            {
                items.Add(new XElement(item.Key.ToString(), item.Value));
            }
            var docBody = new XElement(rootName??"StringDictionary", items.ToArray());
            doc.Add(docBody);
            return doc;
        }
		/// <summary>
		/// Convert a Dictionary{string,object} to a xml document
		/// </summary>
		/// <param name="dic"></param>
		/// <param name="rootName"></param>
		/// <returns></returns>
        public static XDocument ToXMLDoc(this Dictionary<string,object> dic, string rootName)
        {
            XDocument doc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));

            var items = new List<XElement>();
            foreach (KeyValuePair<string,object> item in dic)
            {
                items.Add(new XElement(item.Key, item.Value));
            }
            var docBody = new XElement(rootName ?? "Dictionary", items.ToArray());
            doc.Add(docBody);
            return doc;
		}
		/// <summary>
		/// serialize an object of type T using xml serilizer,and save it to file 
		/// </summary>
		public static void ToXMLDoc<T>(this T obj, string filePath)
		{
			var doc = obj.ToXMLDoc();
			doc.Save(filePath);
			doc = null;
		}
		#endregion 

		#region Deserialize xml data to object
		/// <summary>
        /// Deserialize a object from a file
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xmlFilePath"></param>
        /// <param name="result"></param>
		public static void DeserializeFromFile<T>(string xmlFilePath, out T result)
		{
			Check.Require(!string.IsNullOrEmpty(xmlFilePath), "Argument 'xmlFilePath' can't be null or empty.");
			Check.Require(File.Exists(xmlFilePath), string.Format("File {0} not exists!", xmlFilePath));
            result = default(T);
            XmlReaderSettings xrSettings = new XmlReaderSettings { CloseInput = true, CheckCharacters = true,IgnoreWhitespace=true};
            XmlReader xr = XmlReader.Create(xmlFilePath,xrSettings);
            XmlSerializer xs = new XmlSerializer(typeof(T));
            try
            {
                result = (T)xs.Deserialize(xr);
            }
            catch (Exception ex)
            {
                //do nothing here
            }
            finally {
                xr.Close();
            }
        }
		/// <summary>
		/// Deserialize a object from a file
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="xmlFilePath"></param>
		/// <returns></returns>
		public static T DeserializeFromFile<T>(string xmlFilePath) {
			var retVal = default(T);
			DeserializeFromFile<T>(xmlFilePath, out retVal);
			return retVal;
		}
		/// <summary>
		/// Deserialize a object from xml string data~
		/// </summary>
		/// <param name="xml"></param>
		/// <returns></returns>
		public static T Deserialize<T>(String xmlDATA)
		{
			Check.Require(!string.IsNullOrEmpty(xmlDATA),"xmlDATA can't be null or empty!");
			Check.Require(xmlDATA.IsXMLable(), string.Format("{0} is not valid xml format string!", xmlDATA));

			xmlDATA = xmlDATA.Trim();//trick
			XmlSerializer serial = new XmlSerializer(typeof(T));
			using (StringReader reader = new StringReader(xmlDATA))
			{
				return (T)serial.Deserialize(reader);
			}
		}

		#endregion 


    }
}
